#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Mutex.hpp"
#include "Cond.hpp"

//构建生产者消费者模型
namespace BlockQueueModule
{
    static const int gacp = 10; //假设最大容量为10

    template <typename T>
    class BlockQueue
    {
    private:
        bool IsFull() {return _q.size() == _cap; }
        bool IsEmpty() {return _q.empty(); }
    public:
        BlockQueue(int cap = gacp)
        : _cap(cap), 
        _cwait_num(0),
        _pwait_num(0)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_productor_cond, nullptr);
            pthread_cond_init(&_consumer_cond, nullptr);
        }
        void Equeue(const T &in) //生产者 添加数据
        {
            pthread_mutex_lock(&_mutex); //先申请锁
            //判断条件是否成立
            while(IsFull()) //对条件进行循环判断, 为了防止伪唤醒, 我们通常使用while
            {
                std::cout << "队列已满, 生产者进入等待...." << std::endl;
                _pwait_num++;
                pthread_cond_wait(&_productor_cond, &_mutex); //等待会将持有的锁释放
                //结束等待, 被唤醒
                _pwait_num--;
                //返回,线程被唤醒会重新申请并持有锁(它会在临界区醒来)
                std::cout << "生产者被唤醒..." << std::endl;
            }
            _q.push(in); //生产
            //只要生产了, 那么就一定有数据, 唤醒消费者
            if(_cwait_num)
            {
                std::cout << "叫醒消费者" << std::endl;
                pthread_cond_signal(&_consumer_cond);
            }
            pthread_mutex_unlock(&_mutex);
        }

        void Pop(T *out) //消费者, 输出型参数
        {
            pthread_mutex_lock(&_mutex);
            while(IsEmpty())
            {
                std::cout << "消费者进入等待..." << std::endl;
                _cwait_num++;
                pthread_cond_wait(&_consumer_cond, &_mutex); 
                _cwait_num--;
                std::cout << "消费者被唤醒..." << std::endl;
            }
            *out = _q.front();
            _q.pop();

            //肯定有空间
            if(_pwait_num)
            {
                std::cout << "叫醒生产者" << std::endl;
                pthread_cond_signal(&_productor_cond);
            }
            pthread_mutex_unlock(&_mutex);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_productor_cond);
            pthread_cond_destroy(&_consumer_cond);
        }
    private:
        std::queue<T> _q; //保存数据的容器, 临界资源
        int _cap; //bq最大容量
        pthread_mutex_t _mutex; //互斥
        pthread_cond_t _productor_cond; //生产者条件变量
        pthread_cond_t _consumer_cond; //消费者条件变量

        int _cwait_num; //消费者等待线程个数
        int _pwait_num; //生产者等待线程个数
    };
}